home *** CD-ROM | disk | FTP | other *** search
/ The Utilities Experience / The Utilities Experience - Volume 1.iso / software / datatypes / aiff / source / asyncio.c < prev    next >
C/C++ Source or Header  |  1978-06-29  |  29KB  |  860 lines

  1. #include <exec/types.h>
  2. #include <exec/memory.h>
  3. #include <dos/dos.h>
  4. #include <dos/dosextens.h>
  5.  
  6. #include <clib/exec_protos.h>
  7. #include <clib/dos_protos.h>
  8.  
  9. #include <pragmas/exec_pragmas.h>
  10. #include <pragmas/dos_pragmas.h>
  11.  
  12. #include "asyncio.h"
  13.  
  14.  
  15. /*****************************************************************************/
  16.  
  17.  
  18. /* this macro lets us long-align structures on the stack */
  19. #define D_S(type,name) char a_##name[sizeof(type)+3]; \
  20.                        type *name = (type *)((LONG)(a_##name+3) & ~3);
  21.  
  22.  
  23. /*****************************************************************************/
  24.  
  25.  
  26. /* send out an async packet to the file system. */
  27. static void SendPacket(AsyncFile *file, APTR arg2)
  28. {
  29. struct ExecBase *SysBase;
  30.  
  31.     SysBase = file->af_SysBase;
  32.  
  33.     file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort;
  34.     file->af_Packet.sp_Pkt.dp_Arg2 = (LONG)arg2;
  35.     PutMsg(file->af_Handler, &file->af_Packet.sp_Msg);
  36.     file->af_PacketPending = TRUE;
  37. }
  38.  
  39.  
  40. /*****************************************************************************/
  41.  
  42.  
  43. /* this function waits for a packet to come back from the file system. If no
  44.  * packet is pending, state from the previous packet is returned. This ensures
  45.  * that once an error occurs, it state is maintained for the rest of the life
  46.  * of the file handle.
  47.  *
  48.  * This function also deals with IO errors, bringing up the needed DOS
  49.  * requesters to let the user retry an operation or cancel it.
  50.  */
  51. static LONG WaitPacket(AsyncFile *file)
  52. {
  53. struct ExecBase *SysBase;
  54. struct DosLibrary *DOSBase;
  55. LONG bytes;
  56.  
  57.     SysBase = file->af_SysBase;
  58.     DOSBase = file->af_DOSBase;
  59.  
  60.     if (file->af_PacketPending)
  61.     {
  62.         while (TRUE)
  63.         {
  64.             /* This enables signalling when a packet comes back to the port */
  65.             file->af_PacketPort.mp_Flags = PA_SIGNAL;
  66.  
  67.             /* Wait for the packet to come back, and remove it from the message
  68.              * list. Since we know no other packets can come in to the port, we can
  69.              * safely use Remove() instead of GetMsg(). If other packets could come in,
  70.              * we would have to use GetMsg(), which correctly arbitrates access in such
  71.              * a case
  72.              */
  73.             Remove((struct Node *)WaitPort(&file->af_PacketPort));
  74.  
  75.             /* set the port type back to PA_IGNORE so we won't be bothered with
  76.              * spurious signals
  77.              */
  78.             file->af_PacketPort.mp_Flags = PA_IGNORE;
  79.  
  80.             /* mark packet as no longer pending since we removed it */
  81.             file->af_PacketPending = FALSE;
  82.  
  83.             bytes = file->af_Packet.sp_Pkt.dp_Res1;
  84.             if (bytes >= 0)
  85.             {
  86.                 /* packet didn't report an error, so bye... */
  87.                 return(bytes);
  88.             }
  89.  
  90.             /* see if the user wants to try again... */
  91.             if (ErrorReport(file->af_Packet.sp_Pkt.dp_Res2,REPORT_STREAM,file->af_File,NULL))
  92.                 return(-1);
  93.  
  94.             /* user wants to try again, resend the packet */
  95.             if (file->af_ReadMode)
  96.                 SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  97.             else
  98.                 SendPacket(file,file->af_Buffers[1 - file->af_CurrentBuf]);
  99.          }
  100.     }
  101.  
  102.     /* last packet's error code, or 0 if packet was never sent */
  103.     SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
  104.  
  105.     return(file->af_Packet.sp_Pkt.dp_Res1);
  106. }
  107.  
  108.  
  109. /*****************************************************************************/
  110.  
  111.  
  112. /* this function puts the packet back on the message list of our
  113.  * message port.
  114.  */
  115. static void RequeuePacket(AsyncFile *file)
  116. {
  117. struct ExecBase *SysBase;
  118.  
  119.     SysBase = file->af_SysBase;
  120.  
  121.     AddHead(&file->af_PacketPort.mp_MsgList,&file->af_Packet.sp_Msg.mn_Node);
  122.     file->af_PacketPending = TRUE;
  123. }
  124.  
  125.  
  126. /*****************************************************************************/
  127.  
  128.  
  129. /* this function records a failure from a synchronous DOS call into the
  130.  * packet so that it gets picked up by the other IO routines in this module
  131.  */
  132. static void RecordSyncFailure(AsyncFile *file)
  133. {
  134. struct DosLibrary *DOSBase;
  135.  
  136.     DOSBase = file->af_DOSBase;
  137.  
  138.     file->af_Packet.sp_Pkt.dp_Res1 = -1;
  139.     file->af_Packet.sp_Pkt.dp_Res2 = IoErr();
  140. }
  141.  
  142.  
  143. /*****************************************************************************/
  144.  
  145.  
  146. AsyncFile *OpenAsyncFromFH(BPTR handle, OpenModes mode, LONG bufferSize, struct ExecBase *SysBase, struct DosLibrary *DOSBase)
  147. {
  148. AsyncFile         *file;
  149. struct FileHandle *fh;
  150. BPTR               lock;
  151. LONG               blockSize;
  152. D_S(struct InfoData,infoData);
  153.  
  154.     file   = NULL;
  155.     lock   = NULL;
  156.  
  157.     if (mode == MODE_READ)
  158.     {
  159.         if (handle)
  160.             lock = DupLockFromFH(handle);
  161.     }
  162.     else
  163.     {
  164.         if (mode == MODE_APPEND)
  165.         {
  166.             /* in append mode, we open for writing, and then seek to the
  167.              * end of the file. That way, the initial write will happen at
  168.              * the end of the file, thus extending it
  169.              */
  170.  
  171.             if (handle)
  172.             {
  173.                 if (Seek(handle,0,OFFSET_END) < 0)
  174.                 {
  175.                     Close(handle);
  176.                     handle = NULL;
  177.                 }
  178.             }
  179.         }
  180.  
  181.         /* we want a lock on the same device as where the file is. We can't
  182.          * use DupLockFromFH() for a write-mode file though. So we get sneaky
  183.          * and get a lock on the parent of the file
  184.          */
  185.         if (handle)
  186.             lock = ParentOfFH(handle);
  187.     }
  188.  
  189.     if (handle)
  190.     {
  191.         /* if it was possible to obtain a lock on the same device as the
  192.          * file we're working on, get the block size of that device and
  193.          * round up our buffer size to be a multiple of the block size.
  194.          * This maximizes DMA efficiency.
  195.          */
  196.  
  197.         blockSize = 512;
  198.         if (lock)
  199.         {
  200.             if (Info(lock,infoData))
  201.             {
  202.                 blockSize  = infoData->id_BytesPerBlock;
  203.                 bufferSize = (((bufferSize + (blockSize*2) - 1) / (blockSize*2)) * (blockSize*2));
  204.             }
  205.             UnLock(lock);
  206.         }
  207.  
  208.         /* now allocate the ASyncFile structure, as well as the read buffers.
  209.          * Add 15 bytes to the total size in order to allow for later
  210.          * quad-longword alignement of the buffers
  211.          */
  212.  
  213.         for(;;)
  214.         {
  215.             if (file = AllocVec(sizeof(AsyncFile) + bufferSize + 15,MEMF_PUBLIC | MEMF_ANY))
  216.                 break;
  217.             else
  218.             {
  219.                 if (bufferSize > blockSize*2)
  220.                     bufferSize -= blockSize*2;
  221.                 else
  222.                     break;
  223.             }
  224.         }
  225.  
  226.         if (file)
  227.         {
  228.             file->af_File      = handle;
  229.             file->af_ReadMode  = (mode == MODE_READ);
  230.             file->af_BlockSize = blockSize;
  231.  
  232.             /* initialize the ASyncFile structure. We do as much as we can here,
  233.              * in order to avoid doing it in more critical sections
  234.              *
  235.              * Note how the two buffers used are quad-longword aligned. This
  236.              * helps performance on 68040 systems with copyback cache. Aligning
  237.              * the data avoids a nasty side-effect of the 040 caches on DMA.
  238.              * Not aligning the data causes the device driver to have to do
  239.              * some magic to avoid the cache problem. This magic will generally
  240.              * involve flushing the CPU caches. This is very costly on an 040.
  241.              * Aligning things avoids the need for magic, at the cost of at
  242.              * most 15 bytes of ram.
  243.              */
  244.  
  245.             fh                     = BADDR(file->af_File);
  246.             file->af_Handler       = fh->fh_Type;
  247.             file->af_BufferSize    = bufferSize / 2;
  248.             file->af_Buffers[0]    = (APTR)(((ULONG)file + sizeof(AsyncFile) + 15) & 0xfffffff0);
  249.             file->af_Buffers[1]    = (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
  250.             file->af_Offset        = file->af_Buffers[0];
  251.             file->af_CurrentBuf    = 0;
  252.             file->af_SeekOffset    = 0;
  253.             file->af_PacketPending = FALSE;
  254.             file->af_CloseFH       = FALSE;
  255.             file->af_SysBase       = SysBase;
  256.             file->af_DOSBase       = DOSBase;
  257.  
  258.             /* this is the port used to get the packets we send out back.
  259.              * It is initialized to PA_IGNORE, which means that no signal is
  260.              * generated when a message comes in to the port. The signal bit
  261.              * number is initialized to SIGB_SINGLE, which is the special bit
  262.              * that can be used for one-shot signalling. The signal will never
  263.              * be set, since the port is of type PA_IGNORE. We'll change the
  264.              * type of the port later on to PA_SIGNAL whenever we need to wait
  265.              * for a message to come in.
  266.              *
  267.              * The trick used here avoids the need to allocate an extra signal
  268.              * bit for the port. It is quite efficient.
  269.              */
  270.  
  271.             file->af_PacketPort.mp_MsgList.lh_Head     = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
  272.             file->af_PacketPort.mp_MsgList.lh_Tail     = NULL;
  273.             file->af_PacketPort.mp_MsgList.lh_TailPred = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
  274.             file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
  275.             file->af_PacketPort.mp_Flags               = PA_IGNORE;
  276.             file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
  277.             file->af_PacketPort.mp_SigTask             = FindTask(NULL);
  278.  
  279.             file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
  280.             file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
  281.             file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
  282.             file->af_Packet.sp_Pkt.dp_Res1          = 0;
  283.             file->af_Packet.sp_Pkt.dp_Res2          = 0;
  284.             file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
  285.             file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
  286.             file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
  287.  
  288.             if (mode == MODE_READ)
  289.             {
  290.                 /* if we are in read mode, send out the first read packet to
  291.                  * the file system. While the application is getting ready to
  292.                  * read data, the file system will happily fill in this buffer
  293.                  * with DMA transfers, so that by the time the application
  294.                  * needs the data, it will be in the buffer waiting
  295.                  */
  296.  
  297.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
  298.                 file->af_BytesLeft             = 0;
  299.                 if (file->af_Handler)
  300.                     SendPacket(file,file->af_Buffers[0]);
  301.             }
  302.             else
  303.             {
  304.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
  305.                 file->af_BytesLeft             = file->af_BufferSize;
  306.             }
  307.         }
  308.     }
  309.  
  310.     return(file);
  311. }
  312.  
  313.  
  314. /*****************************************************************************/
  315.  
  316.  
  317. AsyncFile *OpenAsync(const STRPTR fileName, OpenModes mode, LONG bufferSize, struct ExecBase *SysBase, struct DosLibrary *DOSBase)
  318. {
  319. AsyncFile         *file;
  320. struct FileHandle *fh;
  321. BPTR               handle;
  322. BPTR               lock;
  323. LONG               blockSize;
  324. D_S(struct InfoData,infoData);
  325.  
  326.     handle = NULL;
  327.     file   = NULL;
  328.     lock   = NULL;
  329.  
  330.     if (mode == MODE_READ)
  331.     {
  332.         if (handle = Open(fileName,MODE_OLDFILE))
  333.             lock = Lock(fileName,ACCESS_READ);
  334.     }
  335.     else
  336.     {
  337.         if (mode == MODE_WRITE)
  338.         {
  339.             handle = Open(fileName,MODE_NEWFILE);
  340.         }
  341.         else if (mode == MODE_APPEND)
  342.         {
  343.             /* in append mode, we open for writing, and then seek to the
  344.              * end of the file. That way, the initial write will happen at
  345.              * the end of the file, thus extending it
  346.              */
  347.  
  348.             if (handle = Open(fileName,MODE_READWRITE))
  349.             {
  350.                 if (Seek(handle,0,OFFSET_END) < 0)
  351.                 {
  352.                     Close(handle);
  353.                     handle = NULL;
  354.                 }
  355.             }
  356.         }
  357.  
  358.         /* we want a lock on the same device as where the file is. We can't
  359.          * use DupLockFromFH() for a write-mode file though. So we get sneaky
  360.          * and get a lock on the parent of the file
  361.          */
  362.         if (handle)
  363.             lock = ParentOfFH(handle);
  364.     }
  365.  
  366.     if (handle)
  367.     {
  368.         /* if it was possible to obtain a lock on the same device as the
  369.          * file we're working on, get the block size of that device and
  370.          * round up our buffer size to be a multiple of the block size.
  371.          * This maximizes DMA efficiency.
  372.          */
  373.  
  374.         blockSize = 512;
  375.         if (lock)
  376.         {
  377.             if (Info(lock,infoData))
  378.             {
  379.                 blockSize  = infoData->id_BytesPerBlock;
  380.                 bufferSize = (((bufferSize + (blockSize*2) - 1) / (blockSize*2)) * (blockSize*2));
  381.             }
  382.             UnLock(lock);
  383.         }
  384.  
  385.         /* now allocate the ASyncFile structure, as well as the read buffers.
  386.          * Add 15 bytes to the total size in order to allow for later
  387.          * quad-longword alignement of the buffers
  388.          */
  389.  
  390.         for(;;)
  391.         {
  392.             if (file = AllocVec(sizeof(AsyncFile) + bufferSize + 15,MEMF_PUBLIC | MEMF_ANY))
  393.                 break;
  394.             else
  395.             {
  396.                 if (bufferSize > blockSize*2)
  397.                     bufferSize -= blockSize*2;
  398.                 else
  399.                     break;
  400.             }
  401.         }
  402.  
  403.         if (file)
  404.         {
  405.             file->af_File      = handle;
  406.             file->af_ReadMode  = (mode == MODE_READ);
  407.             file->af_BlockSize = blockSize;
  408.  
  409.             /* initialize the ASyncFile structure. We do as much as we can here,
  410.              * in order to avoid doing it in more critical sections
  411.              *
  412.              * Note how the two buffers used are quad-longword aligned. This
  413.              * helps performance on 68040 systems with copyback cache. Aligning
  414.              * the data avoids a nasty side-effect of the 040 caches on DMA.
  415.              * Not aligning the data causes the device driver to have to do
  416.              * some magic to avoid the cache problem. This magic will generally
  417.              * involve flushing the CPU caches. This is very costly on an 040.
  418.              * Aligning things avoids the need for magic, at the cost of at
  419.              * most 15 bytes of ram.
  420.              */
  421.  
  422.             fh                     = BADDR(file->af_File);
  423.             file->af_Handler       = fh->fh_Type;
  424.             file->af_BufferSize    = bufferSize / 2;
  425.             file->af_Buffers[0]    = (APTR)(((ULONG)file + sizeof(AsyncFile) + 15) & 0xfffffff0);
  426.             file->af_Buffers[1]    = (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
  427.             file->af_Offset        = file->af_Buffers[0];
  428.             file->af_CurrentBuf    = 0;
  429.             file->af_SeekOffset    = 0;
  430.             file->af_PacketPending = FALSE;
  431.             file->af_CloseFH       = TRUE;
  432.             file->af_SysBase       = SysBase;
  433.             file->af_DOSBase       = DOSBase;
  434.  
  435.             /* this is the port used to get the packets we send out back.
  436.              * It is initialized to PA_IGNORE, which means that no signal is
  437.              * generated when a message comes in to the port. The signal bit
  438.              * number is initialized to SIGB_SINGLE, which is the special bit
  439.              * that can be used for one-shot signalling. The signal will never
  440.              * be set, since the port is of type PA_IGNORE. We'll change the
  441.              * type of the port later on to PA_SIGNAL whenever we need to wait
  442.              * for a message to come in.
  443.              *
  444.              * The trick used here avoids the need to allocate an extra signal
  445.              * bit for the port. It is quite efficient.
  446.              */
  447.  
  448.             file->af_PacketPort.mp_MsgList.lh_Head     = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
  449.             file->af_PacketPort.mp_MsgList.lh_Tail     = NULL;
  450.             file->af_PacketPort.mp_MsgList.lh_TailPred = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
  451.             file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
  452.             file->af_PacketPort.mp_Flags               = PA_IGNORE;
  453.             file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
  454.             file->af_PacketPort.mp_SigTask             = FindTask(NULL);
  455.  
  456.             file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
  457.             file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
  458.             file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
  459.             file->af_Packet.sp_Pkt.dp_Res1          = 0;
  460.             file->af_Packet.sp_Pkt.dp_Res2          = 0;
  461.             file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
  462.             file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
  463.             file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
  464.  
  465.             if (mode == MODE_READ)
  466.             {
  467.                 /* if we are in read mode, send out the first read packet to
  468.                  * the file system. While the application is getting ready to
  469.                  * read data, the file system will happily fill in this buffer
  470.                  * with DMA transfers, so that by the time the application
  471.                  * needs the data, it will be in the buffer waiting
  472.                  */
  473.  
  474.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
  475.                 file->af_BytesLeft             = 0;
  476.                 if (file->af_Handler)
  477.                     SendPacket(file,file->af_Buffers[0]);
  478.             }
  479.             else
  480.             {
  481.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
  482.                 file->af_BytesLeft             = file->af_BufferSize;
  483.             }
  484.         }
  485.         else
  486.         {
  487.             Close(handle);
  488.         }
  489.     }
  490.  
  491.     return(file);
  492. }
  493.  
  494.  
  495. /*****************************************************************************/
  496.  
  497.  
  498. LONG CloseAsync(AsyncFile *file)
  499. {
  500. struct ExecBase *SysBase;
  501. struct DosLibrary *DOSBase;
  502. LONG result;
  503.  
  504.     if (file)
  505.     {
  506.         SysBase = file->af_SysBase;
  507.         DOSBase = file->af_DOSBase;
  508.  
  509.         result = WaitPacket(file);
  510.         if (result >= 0)
  511.         {
  512.             if (!file->af_ReadMode)
  513.             {
  514.                 /* this will flush out any pending data in the write buffer */
  515.                 if (file->af_BufferSize > file->af_BytesLeft)
  516.                     result = Write(file->af_File,file->af_Buffers[file->af_CurrentBuf],file->af_BufferSize - file->af_BytesLeft);
  517.             }
  518.         }
  519.  
  520.         if (file->af_CloseFH)
  521.             Close(file->af_File);
  522.  
  523.         FreeVec(file);
  524.     }
  525.     else
  526.     {
  527.         result = -1;
  528.     }
  529.  
  530.     return(result);
  531. }
  532.  
  533.  
  534. /*****************************************************************************/
  535.  
  536.  
  537. LONG ReadAsync(AsyncFile *file, APTR buffer, LONG numBytes)
  538. {
  539. struct ExecBase *SysBase;
  540. LONG totalBytes;
  541. LONG bytesArrived;
  542.  
  543.     SysBase = file->af_SysBase;
  544.     totalBytes = 0;
  545.  
  546.     /* if we need more bytes than there are in the current buffer, enter the
  547.      * read loop
  548.      */
  549.  
  550.     while (numBytes > file->af_BytesLeft)
  551.     {
  552.         /* drain buffer */
  553.         CopyMem(file->af_Offset,buffer,file->af_BytesLeft);
  554.  
  555.         numBytes           -= file->af_BytesLeft;
  556.         buffer              = (APTR)((ULONG)buffer + file->af_BytesLeft);
  557.         totalBytes         += file->af_BytesLeft;
  558.         file->af_BytesLeft  = 0;
  559.  
  560.         bytesArrived = WaitPacket(file);
  561.         if (bytesArrived <= 0)
  562.         {
  563.             if (bytesArrived == 0)
  564.                 return(totalBytes);
  565.  
  566.             return(-1);
  567.         }
  568.  
  569.         /* ask that the buffer be filled */
  570.         SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  571.  
  572.         if (file->af_SeekOffset > bytesArrived)
  573.             file->af_SeekOffset = bytesArrived;
  574.  
  575.         file->af_Offset      = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + file->af_SeekOffset);
  576.         file->af_CurrentBuf  = 1 - file->af_CurrentBuf;
  577.         file->af_BytesLeft   = bytesArrived - file->af_SeekOffset;
  578.         file->af_SeekOffset  = 0;
  579.     }
  580.  
  581.     CopyMem(file->af_Offset,buffer,numBytes);
  582.     file->af_BytesLeft -= numBytes;
  583.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  584.  
  585.     return (totalBytes + numBytes);
  586. }
  587.  
  588.  
  589. /*****************************************************************************/
  590.  
  591.  
  592. LONG ReadCharAsync(AsyncFile *file)
  593. {
  594. unsigned char ch;
  595.  
  596.     if (file->af_BytesLeft)
  597.     {
  598.         /* if there is at least a byte left in the current buffer, get it
  599.          * directly. Also update all counters
  600.          */
  601.  
  602.         ch = *(char *)file->af_Offset;
  603.         file->af_BytesLeft--;
  604.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  605.  
  606.         return((LONG)ch);
  607.     }
  608.  
  609.     /* there were no characters in the current buffer, so call the main read
  610.      * routine. This has the effect of sending a request to the file system to
  611.      * have the current buffer refilled. After that request is done, the
  612.      * character is extracted for the alternate buffer, which at that point
  613.      * becomes the "current" buffer
  614.      */
  615.  
  616.     if (ReadAsync(file,&ch,1) > 0)
  617.         return((LONG)ch);
  618.  
  619.     /* We couldn't read above, so fail */
  620.  
  621.     return(-1);
  622. }
  623.  
  624.  
  625. /*****************************************************************************/
  626.  
  627.  
  628. LONG WriteAsync(AsyncFile *file, APTR buffer, LONG numBytes)
  629. {
  630. struct ExecBase *SysBase;
  631. LONG totalBytes;
  632.  
  633.     SysBase = file->af_SysBase;
  634.     totalBytes = 0;
  635.  
  636.     while (numBytes > file->af_BytesLeft)
  637.     {
  638.         /* this takes care of NIL: */
  639.         if (!file->af_Handler)
  640.         {
  641.             file->af_Offset    = file->af_Buffers[0];
  642.             file->af_BytesLeft = file->af_BufferSize;
  643.             return(numBytes);
  644.         }
  645.  
  646.         if (file->af_BytesLeft)
  647.         {
  648.             CopyMem(buffer,file->af_Offset,file->af_BytesLeft);
  649.  
  650.             numBytes   -= file->af_BytesLeft;
  651.             buffer      = (APTR)((ULONG)buffer + file->af_BytesLeft);
  652.             totalBytes += file->af_BytesLeft;
  653.         }
  654.  
  655.         if (WaitPacket(file) < 0)
  656.             return(-1);
  657.  
  658.         /* send the current buffer out to disk */
  659.         SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  660.  
  661.         file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  662.         file->af_Offset     = file->af_Buffers[file->af_CurrentBuf];
  663.         file->af_BytesLeft  = file->af_BufferSize;
  664.     }
  665.  
  666.     CopyMem(buffer,file->af_Offset,numBytes);
  667.     file->af_BytesLeft -= numBytes;
  668.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  669.  
  670.     return (totalBytes + numBytes);
  671. }
  672.  
  673.  
  674. /*****************************************************************************/
  675.  
  676.  
  677. LONG WriteCharAsync(AsyncFile *file, UBYTE ch)
  678. {
  679.     if (file->af_BytesLeft)
  680.     {
  681.         /* if there's any room left in the current buffer, directly write
  682.          * the byte into it, updating counters and stuff.
  683.          */
  684.  
  685.         *(UBYTE *)file->af_Offset = ch;
  686.         file->af_BytesLeft--;
  687.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  688.  
  689.         /* one byte written */
  690.         return(1);
  691.     }
  692.  
  693.     /* there was no room in the current buffer, so call the main write
  694.      * routine. This will effectively send the current buffer out to disk,
  695.      * wait for the other buffer to come back, and then put the byte into
  696.      * it.
  697.      */
  698.  
  699.     return(WriteAsync(file,&ch,1));
  700. }
  701.  
  702.  
  703. /*****************************************************************************/
  704.  
  705.  
  706. LONG SeekAsync(AsyncFile *file, LONG position, SeekModes mode)
  707. {
  708. struct DosLibrary *DOSBase;
  709. LONG  current, target;
  710. LONG  minBuf, maxBuf;
  711. LONG  bytesArrived;
  712. LONG  diff;
  713. LONG  filePos;
  714. LONG  roundTarget;
  715. D_S(struct FileInfoBlock,fib);
  716.  
  717.     DOSBase = file->af_DOSBase;
  718.  
  719.     bytesArrived = WaitPacket(file);
  720.  
  721.     if (bytesArrived < 0)
  722.         return(-1);
  723.  
  724.     if (file->af_ReadMode)
  725.     {
  726.         /* figure out what the actual file position is */
  727.         filePos = Seek(file->af_File,0,OFFSET_CURRENT);
  728.         if (filePos < 0)
  729.         {
  730.             RecordSyncFailure(file);
  731.             return(-1);
  732.         }
  733.  
  734.         /* figure out what the caller's file position is */
  735.         current = filePos - (file->af_BytesLeft+bytesArrived) + file->af_SeekOffset;
  736.         file->af_SeekOffset = 0;
  737.  
  738.         /* figure out the absolute offset within the file where we must seek to */
  739.         if (mode == MODE_CURRENT)
  740.         {
  741.             target = current + position;
  742.         }
  743.         else if (mode == MODE_START)
  744.         {
  745.             target = position;
  746.         }
  747.         else /* if (mode == MODE_END) */
  748.         {
  749.             if (!ExamineFH(file->af_File,fib))
  750.             {
  751.                 RecordSyncFailure(file);
  752.                 return(-1);
  753.             }
  754.  
  755.             target = fib->fib_Size + position;
  756.         }
  757.  
  758.         /* figure out what range of the file is currently in our buffers */
  759.         minBuf = current - (LONG)((ULONG)file->af_Offset - (ULONG)file->af_Buffers[file->af_CurrentBuf]);
  760.         maxBuf = current + file->af_BytesLeft + bytesArrived;  /* WARNING: this is one too big */
  761.  
  762.         diff = target - current;
  763.  
  764.         if ((target < minBuf) || (target >= maxBuf))
  765.         {
  766.             /* the target seek location isn't currently in our buffers, so
  767.              * move the actual file pointer to the desired location, and then
  768.              * restart the async read thing...
  769.              */
  770.  
  771.             /* this is to keep our file reading block-aligned on the device.
  772.              * block-aligned reads are generally quite a bit faster, so it is
  773.              * worth the trouble to keep things aligned
  774.              */
  775.             roundTarget = (target / file->af_BlockSize) * file->af_BlockSize;
  776.  
  777.             if (Seek(file->af_File,roundTarget-filePos,OFFSET_CURRENT) < 0)
  778.             {
  779.                 RecordSyncFailure(file);
  780.                 return(-1);
  781.             }
  782.  
  783.             SendPacket(file,file->af_Buffers[0]);
  784.  
  785.             file->af_SeekOffset = target-roundTarget;
  786.             file->af_BytesLeft  = 0;
  787.             file->af_CurrentBuf = 0;
  788.             file->af_Offset     = file->af_Buffers[0];
  789.         }
  790.         else if ((target < current) || (diff <= file->af_BytesLeft))
  791.         {
  792.             /* one of the two following things is true:
  793.              *
  794.              * 1. The target seek location is within the current read buffer,
  795.              * but before the current location within the buffer. Move back
  796.              * within the buffer and pretend we never got the pending packet,
  797.              * just to make life easier, and faster, in the read routine.
  798.              *
  799.              * 2. The target seek location is ahead within the current
  800.              * read buffer. Advance to that location. As above, pretend to
  801.              * have never received the pending packet.
  802.              */
  803.  
  804.             RequeuePacket(file);
  805.  
  806.             file->af_BytesLeft -= diff;
  807.             file->af_Offset     = (APTR)((ULONG)file->af_Offset + diff);
  808.         }
  809.         else
  810.         {
  811.             /* at this point, we know the target seek location is within
  812.              * the buffer filled in by the packet that we just received
  813.              * at the start of this function. Throw away all the bytes in the
  814.              * current buffer, send a packet out to get the async thing going
  815.              * again, readjust buffer pointers to the seek location, and return
  816.              * with a grin on your face... :-)
  817.              */
  818.  
  819.             diff -= file->af_BytesLeft;
  820.  
  821.             SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  822.  
  823.             file->af_Offset    = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + diff);
  824.             file->af_BytesLeft = bytesArrived - diff;
  825.         }
  826.     }
  827.     else
  828.     {
  829.         if (file->af_BufferSize > file->af_BytesLeft)
  830.         {
  831.             if (Write(file->af_File,file->af_Buffers[file->af_CurrentBuf],file->af_BufferSize - file->af_BytesLeft) < 0)
  832.             {
  833.                 RecordSyncFailure(file);
  834.                 return(-1);
  835.             }
  836.         }
  837.  
  838.         /* this will unfortunately generally result in non block-aligned file
  839.          * access. We could be sneaky and try to resync our file pos at a
  840.          * later time, but we won't bother. Seeking in write-only files is
  841.          * relatively rare (except when writing IFF files with unknown chunk
  842.          * sizes, where the chunk size has to be written after the chunk data)
  843.          */
  844.  
  845.         current = Seek(file->af_File,position,mode);
  846.  
  847.         if (current < 0)
  848.         {
  849.             RecordSyncFailure(file);
  850.             return(-1);
  851.         }
  852.  
  853.         file->af_BytesLeft  = file->af_BufferSize;
  854.         file->af_CurrentBuf = 0;
  855.         file->af_Offset     = file->af_Buffers[0];
  856.     }
  857.  
  858.     return(current);
  859. }
  860.